#pragma once                                                                                                          
#include <vector>
#include <unistd.h>
#include <sys/select.h>
#include "server.hpp"
#include "InetAddr.hpp"

namespace SelectServer 
{
    class selectserver
    {
    public:    
        selectserver(uint16_t port) : _port(port), fds_arry(1024, -1)
        {   }
    
        // 初始化select服务器    
        void InitServer()
        {
            _listensock = TcpSocket::server::Socket();
            TcpSocket::server::Bind(_listensock, _port);
            TcpSocket::server::Listen(_listensock);
        }    

        // select服务器运行
        void Run()
        {    
            fds_arry[0] = _listensock;
            while (true)
            {    
                fd_set readfds;
                FD_ZERO(&readfds);
                // 设置等待时间10秒+5000微妙，若对应文件描述符的IO还未就绪，进程会继续往下运行
                struct timeval time = { 10, 5000 };
                // time也可以用下面进行初始化
                /*time.tv_sec = 5;
                time.tv_usec = 100;*/
                int max_fd = 0;

                // 将read_arry数组中的文件描述符设置进程fds_arry位图中
                for (int i = 0; i < 1024; i++)
                {    
                    if (fds_arry[i] != -1)
                    {    
                        FD_SET(fds_arry[i], &readfds);    
                        if(max_fd < fds_arry[i]) max_fd = fds_arry[i];
                    }
                }

                // 调用select对readfds中的文件描述符进行等待数据
                switch(select(max_fd + 1, &readfds, NULL, NULL, &time))
                {
                case 0:
                    // 没有一个文件描述符的读事件就绪
                    cout << "select timeout" << endl;
                    break;
                case -1:
                    // select失败
                    cout << "select error" << endl;
                default:
                    // 有读事件发生
                    Soluation(readfds);
                    break;
                }           
            }
        }

        // 处理已就绪的读事件
        void Soluation(fd_set& readfds)
        {
            for (int i = 0; i < fds_arry.size(); i++)
            {
                if (FD_ISSET(fds_arry[i], &readfds))
                {
                    if(fds_arry[i] == _listensock)
                    {
                        // 有新链接到来
                        struct sockaddr_in peer;
                        socklen_t len;
                        int sockfd = accept(_listensock, (struct sockaddr*)&peer, &len); 
                        cout << "accept successful" << endl;
                        // 将新链接设置进readfds_arry数组中
                        InetAddr clientaddr(peer);
                        printf("Get a new Link, sockfd: %d, client addr %s:%d\n", sockfd, clientaddr.Ip().c_str(), clientaddr.Port());
                        AddfdsArry(sockfd);
                    }
                    else
                    {
                        //其他事件就绪
                        char buffer[1024];
                        int n = recv(fds_arry[i], &buffer, sizeof(buffer) - 1, 0);
                        switch (n)
                        {
                        case -1:
                            // 读取失败
                            cout << fds_arry[i] << " :recv error" << endl;
                            close(fds_arry[i]);
                            fds_arry[i] = -1;
                            break;
                        case 0:
                            // 对端关闭
                            cout << fds_arry[i] << " :is closed" << endl;
                            close(fds_arry[i]);
                            fds_arry[i] = -1;
                            break;
                        default:
                            // 正常接收对端信息
                            buffer[n] = '\0';
                            printf("[client: %d]# %s", fds_arry[i], buffer);
                            string str = "[selectserver]: 数据已收到";
                            send(fds_arry[i], str.c_str(), sizeof(str), 0);
                            break;
                        }
                    }
                }
            }
        }

        // 将新的sockfd添加到辅助数组中
        void AddfdsArry(int sockfd)
        {
            int pos = 0;
            while (pos < fds_arry.size())
            {
                if (fds_arry[pos] == -1)
                {
                    fds_arry[pos] = sockfd;
                    printf("%d add to select array!\n", sockfd);
                    break;
                }
                pos++;
            }
            if (pos == fds_arry.size())
            {
                close(sockfd);
                cout << "selectserver is full" << endl;
            }
        }
    private:
        int _listensock; // 监听套接字    
        uint16_t _port;  // 端口号  
        vector<int> fds_arry;  // 存放读事件的文件描述符
    };
}
